/*
*
*
*/

#define ACQ_INTERVAL  4000  // milliseconds
#define SERIAL_DEBUG_HEADER  'D'   // Header tag for serial time sync message

/*
#define TIME_STEP      50    // internal time resolution
#define MIN_FREQ      300  //Hz
#define MIN_VOLTAGE  1000  //milivolts
#define MAX_FREQ     3000  //Hz
#define MAX_VOLTAGE  4000  //milivolts
*/


int inPin       = 1;
int outPin      = 13;

double maxFreq = 3000;
double minFreq = 300;
double maxVolt = 4.0;
double minVolt = 1.0;

double FF = (maxFreq - minFreq) / (maxVolt - minVolt);
double F0 = (minFreq*maxVolt - maxFreq*minVolt) / (maxVolt-minVolt);


// debug
long t0;
long ti;
long ti_1;
long te;
long t01;
long t02;
long t03;
long t04;

int nCycles;

// The setup() method runs once, when the sketch starts
void setup()   {                
  t0 = micros();
  // initialize the digital pin as an output:
  pinMode(outPin, OUTPUT);     
  // initialization  
  //digitalWrite(outPin, HIGH);
  //te = micros();
  
  // debug
  Serial.begin(9600);
}
// the loop() method runs over and over again,
// as long as the Arduino has power

void loop()                     
{
  ti_1 = ti;
  ti = micros();
  debugSerial();
/*
  // serial
  if(Serial.available() ){
    processSerial();
  }
*/
  // determine wave semi period 
  double T = obtainPeriod()/2;
  long mT = 1000 * T; 
  Serial.print("mT:");Serial.print(mT);Serial.print(":");
  mT = mT * 0.97;  // correction to improve accuracy
  Serial.println(mT);
  long counterStart = millis();
  boolean isFirst = true;
  // square wave generation for ACQ_INTERVAL cycles 
  nCycles = 0;
  do{
    //TODO: reduce first pulse duration to compensate acq time spent
    if (isFirst) {
      digitalWrite(outPin, HIGH);
      t01 = micros();
      //delay(T);
      delayMicroseconds(mT);
      //preciseDelayMicros(mT, te);
      digitalWrite(outPin, LOW);
      t02 = micros();
      delayMicroseconds(mT);
    } else {
      digitalWrite(outPin, HIGH);
      t03 = micros();
      delayMicroseconds(mT);
      digitalWrite(outPin, LOW);
      t04 = micros();
      delayMicroseconds(mT);
    }
    nCycles++;
    isFirst = false;
   } while (! timeIntervalExceeded(counterStart, ACQ_INTERVAL) );
  //digitalWrite(outPin, HIGH);   // set HIGH during ACQ time
  te = micros();
}

void preciseDelayMicros(long microSeconds, long startedMicros){
  long ts = micros();
  long fixed = microSeconds - (ts - startedMicros);
  Serial.print("preciseDelayMicros"); Serial.print(microSeconds); Serial.print(':'); 
  Serial.print(startedMicros); Serial.print(':'); 
  Serial.print(ts); Serial.print(':'); 
  Serial.println(fixed);
  delayMicroseconds(fixed);
}

void preciseDelay(long ms, long timestamp){
  long ts = micros();
  long fixed =  ms - (ts-timestamp)/1000;
  Serial.print("preciseDelay:");Serial.print(ms); Serial.print(':');Serial.print(timestamp); Serial.print(':');
  Serial.println(fixed);
  fixed =  ms - (ts-timestamp)/1000;
  if (fixed <0) fixed = ms;
  delay(fixed); 
}

double obtainDummyPeriod(){
  int p = millis()/1000 % 10 +1;
  //return p*100;
  return 250;
}

//
double obtainPeriod(){
  int aV = analogRead(inPin); //0-1023 : 0-5V
  Serial.print("analogInput:"); Serial.println(aV);
//  aV = 200;
  double v = 5.0 * aV / 1023;
  double frequency = F0 + FF * v;
  double period = 1000/frequency; // milliseconds
//  Serial.print("FO:");Serial.println(F0);
//  Serial.print("FF:");Serial.println(FF);
  Serial.print("f:");Serial.print(frequency); Serial.print(':');Serial.println(period*1000/2);
//  Serial.print("period:");Serial.println(period);
  return period;
  //return 10;
}

boolean timeIntervalExceeded(long startMillis, int millisDuration){
  //TODO: fix overflow problem
  long currentCounter = millis();
  if ( currentCounter-startMillis > millisDuration)
    return true;
  else
    return false;
}

void debugSerial(){
      //Serial.print(t0); Serial.print(':');Serial.print(ti_1);
      //Serial.print(':'); Serial.print(t01); Serial.print(':'); Serial.print(t02);Serial.print(t03); Serial.print(':'); Serial.print(t04);
      //Serial.print(':'); Serial.println(te);
      //Serial.println(te-ti_1);
      //Serial.print("outside loop:"); Serial.println(ti-te); approx. 8 microSeconds
      Serial.println(t02-t01);
      Serial.println(t04-t03);      
      Serial.println( (te-ti_1)/(nCycles*2));
}


void processSerial() {
  if (Serial.available() >=  0 ){
    char c = Serial.read() ; 
    if( c == SERIAL_DEBUG_HEADER ) {       
      debugSerial();
    }
  }
}
